package com.fx.service;

import com.fx.pojo.IotBean;
import com.fx.service.impl.IotServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author: 梁峰源
 * @date: 2021/10/11 17:08
 * Description:
 */
@Service
@EnableScheduling
public class ClientService {

    Logger logger = LoggerFactory.getLogger(getClass());

    //与服务器通信的异步Channel
    private AsynchronousSocketChannel channel;
    private ExecutorService executor = Executors.newFixedThreadPool(20);

    private String address = "192.168.1.200";//"192.168.0.103";//"localhost";
    private String port = "6006";//6004~6008

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    IotService iotService=new IotServiceImpl();

    /***
     * socket连接
     * ***/
    public void connect() throws Exception {
        ByteBuffer buff = ByteBuffer.allocate(1024);
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //以指定线程池来创建一一个AsynchronousChannelGroup
        AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(pool);
        //以channelGroup作为组管理器来创建AsynchronousSocketChannel
        channel = AsynchronousSocketChannel.open(channelGroup);
        channel.connect(new InetSocketAddress(address, Integer.parseInt(port))).get(3, TimeUnit.SECONDS);
        buff.clear();
        channel.read(buff, null, new CompletionHandler<Integer, Object>() {
            private int countNull;      //记录传递过来的数据是空的次数
            private long currentTime = System.currentTimeMillis();

            @Override
            public void completed(Integer result, Object attachment) {
                buff.flip();
                String content = StandardCharsets.UTF_8.decode(buff).toString().trim();
                maxCount(content);

                try {
                    logger.info("============="+content);//打印输出内容
                    /**   he和ie用来区分两块不同的板子；第二位，d表示对方发给己方，c表示己方发给对方
                     * 1. Hwdhe0111t+27.1h65.1T 温度和湿度Hwdhe0111t+22.9h57.9T
                     * 2. Hwdie0206000160lightT光照强度
                     * 3. Hwdhe0106fst1onT 风扇1
                     * 4. Hwdie0206motor0T 电机，T前面的0表示关闭，1表示正转，2表示反转
                     * 5. Hwdie0206light0T 灯，0表示关闭
                     * 6. Hwdhe015auto0T 自动控制，关
                     * 7. Hwdie0209maxXXXXXXT 温度阈值
                     * 8. Hwdie0211XXXXXXlightT 光照阈值
                     */
                    String type = content.substring(3, 5);
                    switch (type){
                        case "he":      //温湿度、风扇、自动开关
                            if(content.contains("Hwdhe0111")){//判断是温度和湿度
                                logger.info("判断是温度和湿度:"+content);
                                int first = content.indexOf("t") + 1;
                                int second = content.indexOf("h",first);
                                String temp = content.substring(first, second);
                                String hum = content.substring(second+1, content.length() - 1);
                                logger.info("temp===="+temp+",,hum===="+hum);
                                simpMessagingTemplate.convertAndSend("/topic/temp",temp);//websocket推送数据 到前台
                                simpMessagingTemplate.convertAndSend("/topic/hum",hum);
                                /**
                                 * 异常情况处理
                                 * 1. 温度值超过阈值1时，开启一个风扇；超过2时，开启两个；低于阈值1时，关闭所有风扇
                                 * 2. 光照低于预警阈值时，控制步进电机正转，五秒后停止，停止后控制可调灯打开
                                 * 3. 光照高于预警阈值时，控制步进电机反转，五秒后停止，停止后控制可调灯关闭
                                 */
                                //将数据写到数据库中
                                iotService.SaveIotData(new IotBean(
                                        "温度&湿度传感器",
                                        LocalDateTime.now().toString(),
                                        "",
                                        temp,
                                        hum));
                                break;
                            }else if("Hwdhe0106".contains(content)){//判断风扇Hwdhe0106fst1onT
                                if(content.contains("fst1")){//判断是风扇1
                                    //判断是开启还是关闭
                                    String str=content.contains("on") ? "on" : "off";
                                    simpMessagingTemplate.convertAndSend("/topic/fan1",str);
                                    break;
                                }else if(content.contains("fst2")){//判断是风扇2
                                    //判断是开启还是关闭
                                    String str=content.contains("on") ? "on" : "off";
                                    simpMessagingTemplate.convertAndSend("/topic/fan2",str);
                                    break;
                                }
                            }else if(content.contains("Hwdhe015")){//Hwdhe015auto0T 自动控制，关
                                //判断是开启还是关闭
                                String str=content.contains("auto1T") ? "on" : "off";
                                simpMessagingTemplate.convertAndSend("/topic/auto",str);
                                break;
                            }

                        case "ie":      //关照强度、光照阀值、温度阀值、电机、灯
                            if(content.contains("Hwdie0206motor")){//判断是电机
                                String str=content.contains("motor1T") ? "on" : "off";
                                simpMessagingTemplate.convertAndSend("/topic/motor",str);
                                break;
                            }else if(content.contains("Hwdie0206light")){//判断是灯
                                String str=content.contains("light1T") ? "on" : "off";
                                simpMessagingTemplate.convertAndSend("/topic/lightSwitch",str);
                                break;
                            }
                            else if(content.contains("Hwdie0206")){//判断是关照强度
                                String str=content.split("0206")[1].split("light")[0];
                                simpMessagingTemplate.convertAndSend("/topic/light",str);
                                iotService.SaveIotData(new IotBean(
                                        "温度&湿度传感器",
                                        LocalDateTime.now().toString(),
                                        str,
                                        "",
                                        ""));
                                break;
                            }else if(content.contains("Hwdie0211")){//判断是光照阀值
                                String str=content.split("0211")[1].split("light")[0];
                                simpMessagingTemplate.convertAndSend("/topic/lightMax",str);
                                break;
                            }else if(content.contains("Hwdie0209max")){//判断是温度阀值
                                String str=content.split("max")[1].split("T")[0];
                                simpMessagingTemplate.convertAndSend("/topic/temMax",str);
                                break;
                            }

                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }

                buff.clear();
                try {
                    channel.read(buff, null, this);
                } catch (NullPointerException n) {
                    logger.error(n.getMessage());
                }
            }
            //假设服务器掉线情况，就会发送无数垃圾数据过来，导致程序卡顿，这里检测发送过来的短时间内的次数，超过十次就让它断开连接
            private void maxCount(String content) {
                if ("".equals(content)) {
                    if (System.currentTimeMillis() - currentTime < 500) {
                        countNull++;
                    } else {
                        countNull = 0;
                    }
                    currentTime = System.currentTimeMillis();
                    if (countNull > 10) {
                        try {
                            disConnect();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        logger.error("超过十次没有收到空数据,就退出");
                    }
                }
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("控制台连接退出");
            }
        });
    }


    @Scheduled(fixedRate=10000)     //每间隔10秒执行一次, 检查是否掉线, 是就让它重新连接至服务器
    private void configureTasks() {
        if (channel == null || !channel.isOpen()){
            try {
                connect();
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * 关闭连接
     */
    public void disConnect() throws IOException {
        if (this.channel != null && channel.isOpen()) {
            channel.shutdownInput();
            channel.shutdownOutput();
            channel.close();
            channel = null;
        }
    }

    /**
     * 该对象销毁（Build）时，销毁与服务器连接
     */
    @PreDestroy
    public void detroyComponent() throws IOException {
        disConnect();
    }

    public AsynchronousSocketChannel getChannel() {
        return channel;
    }
}
